package cn.elegent.tx.aspect;


import cn.elegent.ac.context.ElegentACContext;
import cn.elegent.tx.annotation.ElegentTx;
import cn.elegent.tx.context.ElegentACTXContext;
import cn.elegent.tx.core.entity.TransactionEntity;
import cn.elegent.tx.core.entity.state.TransactionState;
import cn.elegent.tx.core.manager.TransactionManager;
import cn.elegent.tx.core.storage.TransactionStorage;
import cn.elegent.ac.util.UUIDUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * TransactionAspect
 * @description 分布式消息分发事务切面
 * @author WGL
 * @date 2022/11/6 11:40
*/
@Aspect
@Component
@Slf4j
public class TransactionAspect {

    @Autowired
    TransactionStorage storage;

    @Autowired
    TransactionManager transactionManager;


    @Pointcut("@annotation(cn.elegent.tx.annotation.ElegentTx)")
    public void transaction() {
    }

    /**
     * 在业务逻辑方法执行前执行代码
     * @param point
     */
    @Before(value = "transaction()")
    private void before(JoinPoint point) {
        //根据切点信息订阅主题
        TransactionEntity transaction = createTransaction(point);
        //记录到ThreadLocal中--供后续方法使用
        ElegentACTXContext.set(transaction);
        //在持久层(storage)记录事务数据
        storage.begin(transaction);
        //设置全局id--避免AC框架自己再生成一个全局id 将当前的事务id传递给AC框架
        ElegentACContext.set(transaction.getTransactionId());
    }

    /**
     * 业务方法成功执行完成之后
     * 1)记录回滚参数
     * 2)成功执行--修改事务的状态事务执行成功
     * @param point
     */
    @AfterReturning(value = "transaction()")
    public void endTransaction(JoinPoint point){
        TransactionEntity transactionEntity = ElegentACTXContext.get();
        storage.saveRollBackArgs(transactionEntity);
        storage.change(transactionEntity, TransactionState.SUCCESS);
    }

    /**
     * 抛异常统一回滚
     * @param point
     */
    @AfterThrowing(value = "transaction()")
    public void errorTransaction(JoinPoint point){
        TransactionEntity transactionEntity = ElegentACTXContext.get();
        log.info("----------使用定时任务发送回滚消息------------1");
        //使用事务管理器发送回滚消息
        transactionManager.sendRollBackMsg(transactionEntity);
        storage.change(transactionEntity,TransactionState.END_ROLL_BACK);
    }

    /**
     * 创建事务对象的方法
     * @param point
     * @return
     */
    private TransactionEntity createTransaction(JoinPoint point) {
        String chainID = ElegentACContext.get();

        TransactionEntity transactionEntity = new TransactionEntity();
        if(chainID==null){
            chainID = UUIDUtils.getUUID();
        }
        log.info("链路id:{} ",chainID);
        try {
            ElegentTx acTransaction = getTransactionInfo(point);
            transactionEntity.setChildName(acTransaction.childName());
            transactionEntity.setMainName(acTransaction.mainName());
            transactionEntity.setTransactionId(chainID);
            return  transactionEntity;
        }catch (Exception e){
            return null;
        }
    }


    /**
     * 获取事务名称
     * @return
     */
    private ElegentTx getTransactionInfo(JoinPoint point) throws NoSuchMethodException {
        Signature signature = point.getSignature();
        MethodSignature msg=(MethodSignature) signature;
        Object target = point.getTarget();
        //获取注解标注的方法
        Method method = target.getClass().getMethod(msg.getName(), msg.getParameterTypes());
        //通过方法获取DataIdempotent注解
        return method.getAnnotation(ElegentTx.class);
    }
}
